#include "H264DecoderFrame.h"

#include <time.h>
#ifdef WIN32
#include <windows.h>
#else
#include <sys/time.h>
#endif


#ifdef WIN32
int gettimeofday(struct timeval *tp, void *tzp)
{
	time_t clock;
	struct tm tm;
	SYSTEMTIME wtm;

	GetLocalTime(&wtm);
	tm.tm_year     = wtm.wYear - 1900;
	tm.tm_mon      = wtm.wMonth - 1;
	tm.tm_mday     = wtm.wDay;
	tm.tm_hour     = wtm.wHour;
	tm.tm_min      = wtm.wMinute;
	tm.tm_sec      = wtm.wSecond;
	tm. tm_isdst   = -1;
	clock = mktime(&tm);
	tp->tv_sec = clock;
	tp->tv_usec = wtm.wMilliseconds * 1000;

	return (0);
}
#endif

H264DecoderFrame::H264DecoderFrame()
:m_pobjAVFormatContext(NULL)
,m_pobjAVCodecCtx(NULL)
,m_pobjAVCodec(NULL)
,m_pobjAVFrame(NULL)
,m_szFrameBuffer(NULL)
,m_pobjAVPacket(NULL)
,m_pobjImgConvertCtx(NULL)
,m_nVideoIndex(-1)
{
	
}

H264DecoderFrame::~H264DecoderFrame()
{

}

int H264DecoderFrame::init()
{
	av_register_all();
	m_pobjAVFormatContext = avformat_alloc_context();
	if (m_pobjAVFormatContext == NULL)
	{
		return -1;
	}
	return 0;
}

int H264DecoderFrame::open(const string& p_objFileName)
{
	int nRet = -1;
	nRet = avformat_open_input(&m_pobjAVFormatContext, p_objFileName.c_str(), NULL, NULL);
	if (nRet != 0)
	{
		return nRet;
	}

	nRet = avformat_find_stream_info(m_pobjAVFormatContext, NULL);
	if(nRet < 0)
	{
		return nRet;
	}

	m_nVideoIndex = -1;

	for(int i = 0; i < m_pobjAVFormatContext->nb_streams; i++) 
	{
		if(m_pobjAVFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			m_nVideoIndex=i;
			break;
		}
	}

	if(m_nVideoIndex == -1)
	{
		return -1;
	}

	m_pobjAVCodecCtx = m_pobjAVFormatContext->streams[m_nVideoIndex]->codec;
	m_pobjAVCodec = avcodec_find_decoder(m_pobjAVCodecCtx->codec_id);
	if(m_pobjAVCodec == NULL)
	{
		return -1;
	}

	nRet = avcodec_open2(m_pobjAVCodecCtx, m_pobjAVCodec, NULL);
	if(nRet < 0)
	{
		return nRet;
	}

#ifdef OLD_VERSION
	m_pobjAVFrame = avcodec_alloc_frame();
#else
	m_pobjAVFrame = av_frame_alloc();
#endif // WIN32
	
	int size = avpicture_get_size(m_pobjAVCodecCtx->pix_fmt, m_pobjAVCodecCtx->width, m_pobjAVCodecCtx->height);  
	m_szFrameBuffer = (uint8_t*)av_malloc(size);  
	if (!m_szFrameBuffer)  
	{
		printf("Could not malloc buffer\n");
		return -1; 
	}
	avpicture_fill((AVPicture *)m_pobjAVFrame, m_szFrameBuffer, m_pobjAVCodecCtx->pix_fmt, m_pobjAVCodecCtx->width, m_pobjAVCodecCtx->height);

	int y_size = m_pobjAVCodecCtx->width * m_pobjAVCodecCtx->height;
	m_pobjAVPacket = (AVPacket *)malloc(sizeof(AVPacket));
	//av_new_packet(m_pobjAVPacket, y_size);
	av_init_packet(m_pobjAVPacket);

	//m_pobjImgConvertCtx = sws_getContext(m_pobjAVCodecCtx->width, m_pobjAVCodecCtx->height, m_pobjAVCodecCtx->pix_fmt, m_pobjAVCodecCtx->width, m_pobjAVCodecCtx->height, PIX_FMT_YUVJ420P, SWS_BICUBIC, NULL, NULL, NULL);
	//if (m_pobjImgConvertCtx == NULL)
	//{
	//	return -1;
	//}
	return 0;
}

int H264DecoderFrame::deCoderOneFrame(char* p_szData, const int p_nDataLen, AVFrame*& p_pobjAVFrame)
{
	int got_picture = 0;

	m_pobjAVPacket->size = p_nDataLen;
	m_pobjAVPacket->data = (uint8_t*)p_szData;

	int nRet = avcodec_decode_video2(m_pobjAVCodecCtx, m_pobjAVFrame, &got_picture, m_pobjAVPacket);
	if(nRet < 0)
	{
		return -1;
	}
	if(got_picture)
	{
		char p[128] = {0};
		memset(p, 0, 128);
		sprintf(p, ".//%d.yuv", 0);
		string strFileName = p;
		//saveYUV(m_pobjAVFrame, strFileName);
		p_pobjAVFrame = m_pobjAVFrame;
		return 0;
	}
	
	return -2;
}

void H264DecoderFrame::close()
{

}

int H264DecoderFrame::deCoderAllFile()
{
	int got_picture = 0;
	struct timeval tpstart;
	struct timeval tpend;
	float timeuse = 0.0;
	float timeuseTotal = 0.0;
	int nFrameNo = 0;
	int nCnt = 0;

	while(true)
	{
		int nRet = av_read_frame(m_pobjAVFormatContext, m_pobjAVPacket);
		if (nRet < 0)
		{
			printf("avcodec_decode_video2 FrameNumber:%d, use:%f s\n", nFrameNo, timeuseTotal/1000000);
			return 0;
		}

		if(m_pobjAVPacket->stream_index == m_nVideoIndex)
		{
			gettimeofday(&tpstart, NULL);
			int nRet = avcodec_decode_video2(m_pobjAVCodecCtx, m_pobjAVFrame, &got_picture, m_pobjAVPacket);
			gettimeofday(&tpend, NULL);
			timeuse = 1000000 * (tpend.tv_sec - tpstart.tv_sec) + tpend.tv_usec - tpstart.tv_usec;
			timeuseTotal += timeuse;

			if(nRet < 0)
			{
				return -1;
			}

			if(got_picture)
			{
				//sws_scale(m_pobjImgConvertCtx, (const uint8_t* const*)m_pobjAVFrame->data, m_pobjAVFrame->linesize, 0, m_pobjAVCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
				//printf("avcodec_decode_video2 Frame:%.4d, use:%f us\n", nFrameNo++, timeuse);
				char p[128] = {0};
				memset(p, 0, 128);
				sprintf(p, ".//%d.yuv", nCnt++);
				string strFileName = p;
				//saveYUV(m_pobjAVFrame, strFileName);
				//AVPictureType (I,P,B)
				double timestamp = (m_pobjAVFrame->pkt_pts) * av_q2d(m_pobjAVFormatContext->streams[m_nVideoIndex]->time_base);
				printf("video_frame n:%d coded_n:%d key_frame:%d pict_type:%d pts:%lld pkt_pts:%lld timestmap:%llf\n", nCnt, m_pobjAVFrame->coded_picture_number, m_pobjAVFrame->key_frame, m_pobjAVFrame->pict_type, m_pobjAVFrame->pts, m_pobjAVFrame->pkt_pts, timestamp);
			}
		}
	}
	return 0;
}

int H264DecoderFrame::saveYUV(AVFrame* p_pobjAVFrame, const string& p_strFilaName)
{
	FILE* fp = fopen(p_strFilaName.c_str(), "wb");
	char* buf = (char*)malloc(m_pobjAVCodecCtx->height * m_pobjAVCodecCtx->width * 3 / 2);
	memset(buf, 0, m_pobjAVCodecCtx->height * m_pobjAVCodecCtx->width * 3 / 2);
	int height = m_pobjAVCodecCtx->height;
	int width = m_pobjAVCodecCtx->width;

	int a = 0;
	for (int i = 0; i < height; i++)
	{
		memcpy(buf + a, p_pobjAVFrame->data[0] + i * p_pobjAVFrame->linesize[0], width);
		a += width;
	}
	for (int i = 0; i<height / 2; i++)
	{
		memcpy(buf + a, p_pobjAVFrame->data[1] + i * p_pobjAVFrame->linesize[1], width / 2);
		a += width / 2;
	}
	for (int i = 0; i<height / 2; i++)
	{
		memcpy(buf + a, p_pobjAVFrame->data[2] + i * p_pobjAVFrame->linesize[2], width / 2);
		a += width / 2;
	}
	fwrite(buf, 1, m_pobjAVCodecCtx->height * m_pobjAVCodecCtx->width * 3 / 2, fp);
	free(buf);
	buf = NULL;
	fclose(fp);
	return 0;
}
